import { _decorator, ICollisionEvent, Node, RigidBody, SphereCollider, Vec3 } from 'cc';
import { Game } from '../core/Game';
import { BilliardGame } from './BilliardGame';
import { BaseComponent } from './BaseComponent';
import { BallAttribute } from '../core/BallAttribute';
import { SoundManager } from '../framework/SoundManager';
import { TableAtrribute } from '../core/TableAttribute';
import { EventManager } from '../core/EventManager';

const { ccclass, property } = _decorator;

@ccclass('BaseBallSphere')
export class BaseBallSphere extends BaseComponent {
  protected radius = BallAttribute.radius;
  protected diameter = BallAttribute.diameter;
  protected ballNode: Node = null; // 当前球的节点
  protected ballBody: RigidBody = null; // 当前球的刚体
  protected ballCollider: SphereCollider = null; // 当前球的碰撞体
  // 是否已经进洞
  public hasInHole = false;
  // 与球洞碰撞或者在球洞中，这个时候需要把速度清0
  public hasClearVelocity = false;
  // 当前的线性速度
  public currentLinearVelocity = new Vec3();
  // 最近有效的位置，用于纠正界外球
  public currentCorrectPosition = new Vec3();

  onLoad() {
    this.ballNode = this.node;
    this.ballBody = this.ballNode.getComponent(RigidBody);
    this.ballCollider = this.ballNode.getComponent(SphereCollider);
    this.ballBody.useGravity = true;
    this.ballBody.useCCD = true;

    this.ballBody.mass = 10;
    this.ballBody.angularDamping = 0.65;
    this.ballBody.linearDamping = 0.25;
    // 设置y的值
    this.node.setWorldPosition(
      this.node.worldPosition.x,
      TableAtrribute.DeskTopAxisY,
      this.node.worldPosition.z,
    );
  }
  start() {
  }
  public resetAngularDamping() {
    this.ballBody.angularDamping = 0.65;
  }
  public setAngularDamping(ad: number) {
    this.ballBody.angularDamping = ad;
  }
  
  // 从球洞中拿出球来
  public outHole() {
    this.hasInHole = false;
    this.node.active = true;
    this.hasClearVelocity = false;
  }

  public update(deltaTime: number) {
    const thisPos: Vec3 = this.node.worldPosition;
    // 球不能飞的太高，不然容易出界
    if (thisPos.y > TableAtrribute.DeskTopAxisY + 0.1) {
      this.node.setWorldPosition(thisPos.x, TableAtrribute.DeskTopAxisY, thisPos.z);
    }
    // y轴不能有正向速度，可以受重力下落，不然容易出边界
    this.ballBody.getLinearVelocity(this.currentLinearVelocity);
    if (this.currentLinearVelocity.y > 0) {
      this.currentLinearVelocity.y = 0;
      this.ballBody.setLinearVelocity(this.currentLinearVelocity);
    }
    this.prepareInHole();
    // 进球的判断
    this.processInHole();
    // 边界处理，球越出最大边界时，可以进行挽救
    this.processDeskEdgeCollision();
  }

  public onCollisionEnter(event: ICollisionEvent) {
    // 碰撞球洞，则清理球速度和受力
    if (this.isHoleName(event.otherCollider.node.name)) {
      if (!this.hasClearVelocity && this.ballNode.active) {
        // 清理速度
        this.ballBody.setLinearVelocity(Vec3.ZERO);
        this.hasClearVelocity = true;
      }
    }
    // 碰撞声音
    if (this.isBallName(event.otherCollider.node.name)) {
      if (event.otherCollider.node.name < this.ballNode.name) {
        this.ballBody.getLinearVelocity(this.currentLinearVelocity);
        const otherBody = event.otherCollider.node.getComponent(RigidBody);
        const velo = new Vec3();
        otherBody.getLinearVelocity(velo);
        const v = this.currentLinearVelocity.x * this.currentLinearVelocity.x + 
          this.currentLinearVelocity.z * this.currentLinearVelocity.z
          + velo.x * velo.x + velo.z * velo.z;
        const volume = Math.min(Math.max(v / 10, 1), 10) / 10;
        SoundManager.Instance().playBallsCollide(volume);
      }
    }
  }
  public onCollisionExit(event: ICollisionEvent) {
  }
  public processDeskEdgeCollision() {
    this.ballBody.getLinearVelocity(this.currentLinearVelocity);
    const missDist = 1.25;
    const missDiff = this.radius;
    // 已经超过了边界，将其拉回来
    if (this.isDeskEdgeCollision(this.node.worldPosition, missDist, missDiff)) {
      this.node.setWorldPosition(this.currentCorrectPosition);
      this.ballBody.setLinearVelocity(this.currentLinearVelocity);
      console.log('desk edge collision ', this.currentCorrectPosition, this.currentLinearVelocity);
    }
  }
  // 判断球是否超出球桌范围，附加返回 【正确的位置和速度】
  private isDeskEdgeCollision(pos: Vec3, dist: number, diff: number) {
    const alpha = 1;
    this.ballBody.getLinearVelocity(this.currentLinearVelocity);
    if (pos.x > TableAtrribute.DeskHalfAxisXLength - alpha * diff) {
      if (pos.z > -TableAtrribute.DeskHalfAxisZLength + dist && 
        pos.z < TableAtrribute.DeskHalfAxisZLength - dist) {
        this.currentCorrectPosition.x = TableAtrribute.DeskHalfAxisXLength - alpha * diff;
        this.currentCorrectPosition.z = pos.z;
        this.currentCorrectPosition.y = TableAtrribute.DeskTopAxisY;
        this.currentLinearVelocity.x = -Math.abs(this.currentLinearVelocity.x);
        return true;
      }
    }
    if (pos.x < -TableAtrribute.DeskHalfAxisXLength + alpha * diff) {
      if (pos.z > -TableAtrribute.DeskHalfAxisZLength + dist &&
        pos.z < TableAtrribute.DeskHalfAxisZLength - dist) {
        this.currentCorrectPosition.x = -TableAtrribute.DeskHalfAxisXLength + alpha * diff;
        this.currentCorrectPosition.z = pos.z;
        this.currentCorrectPosition.y = TableAtrribute.DeskTopAxisY;
        this.currentLinearVelocity.x = Math.abs(this.currentLinearVelocity.x);
        return true;
      }
    }
    if (pos.z > TableAtrribute.DeskHalfAxisZLength - alpha * diff) {
      if (pos.x > -TableAtrribute.DeskHalfAxisXLength + dist && pos.x < -dist) {
        this.currentCorrectPosition.x = pos.x;
        this.currentCorrectPosition.z = TableAtrribute.DeskHalfAxisZLength - alpha * diff;
        this.currentCorrectPosition.y = TableAtrribute.DeskTopAxisY;
        this.currentLinearVelocity.z = -Math.abs(this.currentLinearVelocity.z);
        return true;
      }
      if (pos.x > dist && pos.x < TableAtrribute.DeskHalfAxisXLength - alpha * dist) {
        this.currentCorrectPosition.x = pos.x;
        this.currentCorrectPosition.z = TableAtrribute.DeskHalfAxisZLength - alpha * diff;
        this.currentCorrectPosition.y = TableAtrribute.DeskTopAxisY;
        this.currentLinearVelocity.z = -Math.abs(this.currentLinearVelocity.z);
        return true;
      }
    }
    if (pos.z < -TableAtrribute.DeskHalfAxisZLength + alpha * diff) {
      if (pos.x > -TableAtrribute.DeskHalfAxisXLength + dist && pos.x < -dist) {
        this.currentCorrectPosition.x = pos.x;
        this.currentCorrectPosition.z = -TableAtrribute.DeskHalfAxisZLength + alpha * diff;
        this.currentCorrectPosition.y = TableAtrribute.DeskTopAxisY;
        this.currentLinearVelocity.z = Math.abs(this.currentLinearVelocity.z);
        return true;
      }
      if (pos.x > dist && pos.x < TableAtrribute.DeskHalfAxisXLength - dist) {
        this.currentCorrectPosition.x = pos.x;
        this.currentCorrectPosition.z = -TableAtrribute.DeskHalfAxisZLength + alpha * diff;
        this.currentCorrectPosition.y = TableAtrribute.DeskTopAxisY;
        this.currentLinearVelocity.z = Math.abs(this.currentLinearVelocity.z);
        return true;
      }
    }
    return false;
  }
  // 预处理快进洞的球
  private prepareInHole() {
    if (this.hasClearVelocity) {
      return;
    }
    if (!this.ballNode.active) {
      return;
    }
    const thisPos = this.node.worldPosition;
    const inHoles = BilliardGame.Instance().getDeskExtractInHolePosition();
    for (const hole of inHoles) {
      const dist = (hole.x - thisPos.x) * (hole.x - thisPos.x) + (hole.z - thisPos.z) * (hole.z - thisPos.z);
      if (dist < 0.09) {
        this.ballBody.setLinearVelocity(Vec3.ZERO);
        this.hasClearVelocity = true;
      }
    }
  }
  // 球进洞
  private processInHole() {
    const thisPos = this.node.worldPosition;
    if (!this.ballNode.active) {
      return;
    }
    if (this.ballNode.worldPosition.y < TableAtrribute.DeskHoleAxisYPosition) {
      this.node.setWorldPosition(thisPos.x, 0, thisPos.z);
      console.log('ball is in holed now ', this.ballNode.name, this.ballNode.worldPosition);
      this.hasInHole = true;
      this.ballNode.active = false;
      SoundManager.Instance().playBallInHoled(0.5);
      Game.Instance().pushHitBallList(this.node.name);
    }
  }
}

